package juego.Tablero;

import java.util.ArrayList;
import java.util.Random;

import juego.JuegoListener;
import juego.Jugador;
import juego.Movimiento;
import juego.Dificultad.*;
import juego.Encontrable.Encontrable;
import juego.Persistencia.EncontrableGuardable;

import java.io.File;
import java.net.URISyntaxException;

import javax.xml.bind.*;

public class Tablero implements InfoTablero{

	private PuntoInterseccion meta;
	private ArrayList<PuntoInterseccion> esquinas;
	private ArrayList<Tramo> tramos;
	private PuntoInterseccion ubicacionVehiculo;
	private int cantidadFilas;
	private int cantidadColumnas;
	private JuegoListener listener;
	private Dificultad dificultad;
	
	public Tablero(Dificultad dificultad) {	
		this.dificultad = dificultad;
		this.cantidadFilas = dificultad.getQFilas();
		this.cantidadColumnas = dificultad.getQColumnas();		

		esquinas = new ArrayList<PuntoInterseccion>();
		tramos = new ArrayList<Tramo>();

		esquinas= new ArrayList<PuntoInterseccion>();
		tramos = new ArrayList<Tramo>();	
		
		crearMapa(esquinas,tramos);
		cargarEncontrables(tramos,dificultad.getConfiguracionEncontrables());
		ubicacionVehiculo = devolverPosicionBordeAleatoria(esquinas);
		meta = devolverPosicionContrariaAleatoria(ubicacionVehiculo,esquinas);		
			
	}		
	
	public void setJuegoListener(JuegoListener listener) {
		this.listener = listener;
	}
	
	private void cargarEncontrables(ArrayList<Tramo> tramos, String configuracionEncontrables){
		try{
			JAXBContext context = JAXBContext.newInstance(DatosDeEncontrables.class);
			Unmarshaller um = context.createUnmarshaller();
			File xmlFile = new File(Tablero.class.getResource(configuracionEncontrables).toURI());
			DatosDeEncontrables datos = (DatosDeEncontrables) um.unmarshal(xmlFile);
		    ArrayList<DatosEncontrable> listaDeDatos = datos.getDatos();
		    for (DatosEncontrable dato : listaDeDatos) {
		    	Encontrable encontrable = dato.devolverEncontrable();
		    	tramos.get(dato.getTramo()).agregarEncontrable(encontrable);
		    }
		} catch (JAXBException e){
			e.printStackTrace();			
		} catch (URISyntaxException e) {
			e.printStackTrace();
		}
	}


	private void crearMapa(ArrayList<PuntoInterseccion> esquinas, ArrayList<Tramo> tramos){
		
		for (int iii=0; iii <= cantidadFilas; iii++){
			for (int jjj=0; jjj <= cantidadColumnas; jjj++){

				esquinas.add(new PuntoInterseccion(iii,jjj));
				if (jjj != 0)
					tramos.add(new Tramo(esquinas.get(iii*(cantidadColumnas+1)+jjj-1),esquinas.get(iii*(cantidadColumnas+1)+jjj)));

				if (iii != 0)
					tramos.add(new Tramo(esquinas.get((iii-1)*(cantidadColumnas+1) + jjj),esquinas.get(iii*(cantidadColumnas+1) +jjj)));					
			}
		}		
	}


	private PuntoInterseccion devolverPosicionContrariaAleatoria( PuntoInterseccion ubicacionVehiculo, ArrayList<PuntoInterseccion> esquinas) {
		
		int coordenadaX,coordenadaY;
				
		if (ubicacionVehiculo.mostrarCoordenadaY() == 0){
			coordenadaY = cantidadColumnas -1;
		} else coordenadaY = 0;
			
		Random generadorRandom = new Random();				
		coordenadaX = generadorRandom.nextInt(cantidadFilas);
				
		return buscarPuntoConCoordenadas(esquinas,coordenadaX,coordenadaY);		
	}

	private PuntoInterseccion devolverPosicionBordeAleatoria (ArrayList<PuntoInterseccion> esquinas){
		int coordenadaX,coordenadaY;
		
		Random generadorRandom = new Random();
		int IzquierdaODerecha = generadorRandom.nextInt(2);
		
		if (IzquierdaODerecha == 0)
			coordenadaY = 0;
		else 
			coordenadaY = cantidadColumnas;
		
		coordenadaX = generadorRandom.nextInt(cantidadFilas);
		return buscarPuntoConCoordenadas(esquinas,coordenadaX,coordenadaY);
	}
	
	
	private PuntoInterseccion buscarPuntoConCoordenadas( ArrayList<PuntoInterseccion> esquinas, int coordenadaX,
			int coordenadaY) {
		
			for(PuntoInterseccion pI: esquinas){
				if ((pI.mostrarCoordenadaX() == coordenadaX) && (pI.mostrarCoordenadaY() == coordenadaY))
							return pI;
			}
			return null;
	}
	
	private ArrayList <Encontrable> buscarTramo (PuntoInterseccion ubicacionVehiculo, PuntoInterseccion destino){
		for (int indice=0; indice<tramos.size(); indice++){
			if (tramos.get(indice).puntosPertenecenATramo(ubicacionVehiculo, destino)) {
				return (tramos.get(indice).devolverEncontrables());
			}
		}
		return null;
	}
	
	
	private PuntoInterseccion buscarDestino(PuntoInterseccion origen, Movimiento movimiento) {
		int coordX = origen.mostrarCoordenadaX();
		int coordY = origen.mostrarCoordenadaY();
		
		switch (movimiento) {
		case arriba:	coordX= coordX - 1;
						break;
		case abajo: 	coordX= coordX + 1;
						break;
		case derecha: 	coordY= coordY + 1;
						break;
		case izquierda: coordY= coordY - 1;
						break;
		}
		
		for (int indice=0; indice<esquinas.size(); indice++){
			if ((esquinas.get(indice).mostrarCoordenadaX() == coordX) && (esquinas.get(indice).mostrarCoordenadaY() == coordY)) {
				if ( esquinas.get(indice).isEstaOcupado() )
					return null;
				return esquinas.get(indice);
			}
		}
		
		return null; // si sale del for sin return es porque el punto no era valido	
	}
	
	
	public boolean mover(Jugador jugadorQueSeMueve,Movimiento aDondeSeMueve) {
		PuntoInterseccion destino = buscarDestino(ubicacionVehiculo,aDondeSeMueve);
		
		// busco el punto de destino y verifico que se pueda mover
		if (destino==null)
			return false;
		
		jugadorQueSeMueve.sumarCantidadDeMovimientos(1);
		
		// busco encontrables que haya en el tramo entre el punto de origen y destino y los atravieso
		
		ArrayList <Encontrable> encontrablesEnTramo = buscarTramo (ubicacionVehiculo, destino);
		for (int indice=0; indice<encontrablesEnTramo.size(); indice++){
			if ( !encontrablesEnTramo.get(indice).atravesar(jugadorQueSeMueve)) {
				listener.refrescar();
				return true;
			}
		}

		ubicacionVehiculo.setEstaOcupado(false);
		destino.setEstaOcupado(true);

		// actualizamos la posicion del Jugador y salimos
		ubicacionVehiculo=destino;
		listener.refrescar();
		return true;
	}

	public boolean confirmarSiGano(Jugador jugador){
		return meta.esMismoPunto(ubicacionVehiculo) ;
	}

	public PuntoInterseccion darPosicionSalida(){
		return  ubicacionVehiculo;
	}
	
	public PuntoInterseccion darPosicionVehiculo(){
		return ubicacionVehiculo;
	} 

	public PuntoInterseccion darPosicionLlegada(){
		return  meta;
	}
	public ArrayList<Tramo> darPosicionEncontrables(){
		return  tramos;
	}
	
	public int darCantidadFilas(){
		return cantidadFilas;
		// necesito q me pases la cant de filas y abajo de columnas q tenga el tablero, 
		//por ej me vendria bien si me podes pasar la cant de manzanas q tiene en fila y col
	}
	public int darCantidadColumnas(){
		return cantidadColumnas;
	}

	public Dificultad darDificultad() {
		return dificultad;
	}
	
	public void setMeta (PuntoInterseccion meta){
		this.meta = meta;
	}
	
	public void setPosicionVehiculo (PuntoInterseccion posicionVehiculo){
		this.ubicacionVehiculo = posicionVehiculo;
	}

	@Override
	public ArrayList<EncontrableGuardable> darEncontrablesDesactivados() {
		ArrayList<EncontrableGuardable> listaEncontrables = new ArrayList<EncontrableGuardable>();
		for (int indice=0; indice<tramos.size(); indice++) {
			ArrayList<Encontrable> encontrables = tramos.get(indice).devolverEncontrables();
			if (encontrables.size()!=0){
				for (int pos=0; pos<encontrables.size(); pos++) {
					if (encontrables.get(pos).estaActivo()== false) {
					EncontrableGuardable encontrable = new EncontrableGuardable();
					encontrable.setTramo(indice);
					encontrable.setPosicion(pos);
					listaEncontrables.add(encontrable);
					}
				}
			}
		}
		return listaEncontrables;
	}

	public void desactivarEncontrables(	ArrayList<EncontrableGuardable> encontrables) {
		
		for (EncontrableGuardable encontrableGuardado:encontrables) {
			ArrayList<Encontrable> listaEncontrables = tramos.get(encontrableGuardado.getTramo()).devolverEncontrables();
			listaEncontrables.get(encontrableGuardado.getPosicion()).desactivar();
		}
		
	}
}
